from datetime import datetime
from aiogram import types
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup
import logging
from app.bot_init import bot
from app.models.announcements import insert_announcements_to_db
from app.models.chat_admins import delete_admins_from_channel, get_channels_by_admin_id

from app.services.keyboards import prepare_keyboard, prepare_inline_keyboard
from app.services.redis_memory import add_announcement_picture, add_announcement_text, get_announcement, set_announcement_channel, \
        add_announcement_date
from app.utils.chat_admin import is_channel_admin
from app.utils.dates import parse_datetime_string


cancel_button = prepare_keyboard(('Отменить',))
start_menu = prepare_keyboard(('Создать анонс', 'Мои анонсы'))
event_prepare_menu = prepare_keyboard(('Опубликовать', 'Отменить', 'Начать заново'))

class AddEvent(StatesGroup):
    waiting_for_message = State()
    waiting_for_date = State()


async def cancel_adding(message: types.Message) -> bool:
    """
    Проверяет, получено ли сообщение Отменить и посылает сообщение ою отмене
    """
    if message.text and message.text.lower() == 'отменить':
        await message.answer('Отменяю анонс, можете попробовать заново или посмотреть \
какие анонсы предстоят', reply_markup=start_menu)
        return True
    return False


async def add_event_start(message: types.Message):
    """
    По нажатию кнопки "Создать анонс" в меню бота высылает инлайн клаву
    с выбором канала, куда постить ивент.
    """
    channels = get_channels_by_admin_id(message.from_user.id)
    channels = [(c["channel_title"], c["channel_id"]) for c in channels]
    if not channels:
        return await message.answer("Не нашел ваших зарегистрированных каналов \
в бвзе данных. Чтобы зарегистрировать канал, добавьте бота администратором в \
канал и напишите в нем команду /register. При добавлении новых администраторов \
в канал, нужно будет ввести команду повторно") 
    channels.append(('Отменить', 'cancel'))
    keyboard = prepare_inline_keyboard(channels, 'channel')
    await message.answer('Начинаем', reply_markup=types.ReplyKeyboardRemove())
    await message.answer(
            'Выберите канал, в который хотите добавить анонс',
            reply_markup=keyboard
    )


async def channel_choosen(call: types.CallbackQuery, state: FSMContext):
    """
    Обрабатывает клик по кнопке с выбором канала для создания анонса,
    переводит в режим ожидания даты и высылает клаву.
    """
    if call.data.split('channel_')[1] == 'cancel':
        await call.message.answer('Отменяю анонс, можете попробовать \
заново или посмотреть какие анонсы предстоят', reply_markup=start_menu)
        return await call.message.delete()
    user_id = call.from_user.id
    try:
        channel_id = int(call.data.split('channel_')[1])
        if not await is_channel_admin(channel_id, user_id):
            delete_admins_from_channel(channel_id, user_id)
            await call.message.answer(
                "Больше вы не являетесь администратором этого канала или не \
можете отправлять в него сообщения. Обратитесь к создателю канала",
                reply_markup=start_menu
            )
            return await state.finish()

        channel_name = ""
        for button in call.message.reply_markup.inline_keyboard:
            if button[0].callback_data == call.data:
                channel_name = button[0].text 
                break
    except Exception:
        return logging.exception('Не верный колбэк с каналом')
    
    try:
        set_announcement_channel(user_id, channel_id)
    except Exception:
        logging.exception("Не получилось записать в редис")
        #TODO разобраться с ошибочным воркфлоу
        return

    await call.message.answer(
            f'Вы выбрали канал <b>{channel_name}</b>.\nВведите дату анонса. \
Например: <b>8-03-23 11:00</b>',
            parse_mode=types.message.ParseMode.HTML,
            reply_markup=cancel_button
    )
    await state.set_state(AddEvent.waiting_for_date.state)
    await call.message.delete()


async def date_choosen(message: types.Message, state: FSMContext):
    """
    Принимает дату для создания анонса, переводит в режим ожидания мессаджа 
    и высылает клаву.
    """
    if await cancel_adding(message):
        return await state.finish()
    user_id = message.from_user.id
    date = parse_datetime_string(message.text)
    if date is not None and date > datetime.now() :
        add_announcement_date(user_id, date)
        await message.answer(
                f'Вы ввели дату <b>{date}</b>\nТеперь введите ваше сообщение, \
добавьте картинку, затем нажмите опубликовать',
                reply_markup=cancel_button,
                parse_mode=types.message.ParseMode.HTML
        )
        await state.set_state(AddEvent.waiting_for_message.state)
    else:
        await message.reply('Не смог распознать дату и время либо дата уже \
прошла. Попробуйте еще раз.\nНапример: 8-03-23 11:00')

async def accept_event_data(message: types.Message, state: FSMContext):
    if await cancel_adding(message):
        return await state.finish()
    user_id = message.from_user.id
    if message.photo: 
        try:
            add_announcement_picture(user_id, message.photo[0].file_id)
        except Exception as error:
            await message.reply(error.args[0] + "\n Обратитесь к разработчику")
        else:
            await message.reply('Добавил/заменил картинку в сообщении')
        finally:
            return
    mess = message.text.lower()
    markup = None
    reply_message = ""
    if mess == 'опубликовать':
        data = get_announcement(user_id)
        reply_message = f"Опубликовано"
        markup = start_menu
        if data:
            #TODO создание шаблонов анонса
            #TODO отправка анонса в канал
            data["posted_by"] = message.from_id
            insert_announcements_to_db(data)
            await bot.send_message(data["channel_id"], "POST")
        else:
            raise ValueError("Возникла проблема с публикацией поста. data is empty")
        await state.finish()
    elif mess == 'начать заново':
        markup = types.ReplyKeyboardRemove()
        reply_message = 'Начинаем сначала'
        await state.finish()
        return await add_event_start(message)
    elif mess == 'отменить':
        markup = start_menu
        reply_message = "Отменяем анонс"
        await state.finish()
    else:
        try:
            add_announcement_text(user_id, message.html_text)
        except Exception as err:
            reply_message = err.args[0]
            markup = start_menu
        else:
            reply_message = f'Добавил/заменил текст анонса'
            markup = event_prepare_menu

    await message.answer(reply_message, reply_markup=markup)
